/*
 * User: rant
 * Date: 14/02/2007
 *
 *
 */
package laMalinche.rete.nodes.tailing;

import laMalinche.LaMaContext;
import laMalinche.rete.rule.ActObservable;
import laMalinche.rete.rule.Condition;
import laMalinche.rete.rule.Progression;
import laMalinche.testingPlayGround.testModel.VOEntity;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 *
 */
public class BasicTailingManager implements TailingManager {
    private static Logger log = LogManager.getLogger(BasicTailingManager.class);
    //todo ran >> change to set ??
    private List<VOEntity> touchedElements = new ArrayList<VOEntity>(10);
    private boolean repeatTailing = true;
    private final List<PropertyTouchedListener> propertyTouchedListeners = new ArrayList<PropertyTouchedListener>();
    private ActObservable actObservable;
    private boolean firstTime = true;
    private final Set<String> excludedFromTailing = new HashSet<String>();
    private Progression progression = Progression.stop;


    private void initializeTouchedElements() {
        //todo  ran >> create object containing all collections
        touchedElements = new ArrayList<VOEntity>(10);
    }

    private AccessReporter getAccessReporter() {
        // get from factory
        return LaMaContext.getInstance().getAccessReporter();
    }

    public void actFinished() {
        if (isRepeatTailing() || firstTime) {
            removeFieldAccessedListener();
            if (!touchedElements.isEmpty()) {
                firstTime = false;
                for (PropertyTouchedListener propertyTouchedListener : propertyTouchedListeners) {
                    propertyTouchedListener.actCompleted(touchedElements);
                }
                progression = Progression.stop;
            }
        }
    }

    public Progression getProgression() {
        return progression;
    }

    public void stopRuleExecution() {
    }

    public ActObservable getActObservable() {
        return actObservable;
    }

    public void actStarted() {
        if (isRepeatTailing() || touchedElements.isEmpty()) {
            initializeTouchedElements();
            addElementAccessedListener();
            progression = Progression.run;
        }
    }

    public void pause() {
        if (progression == Progression.run) {
            removeFieldAccessedListener();
            progression = Progression.pause;
        }
    }

    public void actContinue() {
        if (progression == Progression.pause) {
            addElementAccessedListener();
            progression = Progression.run;
        }
    }

    private boolean isRepeatTailing() {
        return repeatTailing;
    }

    protected void addElementAccessedListener() {
        getAccessReporter().addAccessListener(this);
    }


    protected void removeFieldAccessedListener() {
        getAccessReporter().removeAccessListener(this);

    }

    public void propertyTouched(VOEntity entity) {
// todo ran >> handle each event differently
        //element.isFieldElement() || element.isListElement()) &&
        if (!excludedFromTailing.contains(entity.getGeneralizeOgnl())) {
            if (!touchedElements.contains(entity)) {
                touchedElements.add(entity);
            }

            for (PropertyTouchedListener propertyTouchedListener : propertyTouchedListeners) {
                propertyTouchedListener.propertyTouched(entity);
            }
        }
    }


    public void setExcludedProperties(Set<String> exclededElements) {
        if (exclededElements != null) {
            excludedFromTailing.addAll(exclededElements);
        }
    }


    public void setActObservable(ActObservable actObservable) {
        this.actObservable = actObservable;
        if (actObservable instanceof Condition && !((Condition) actObservable).isDisjunction()) {
            repeatTailing = false;
        }
    }

    public void addPropertyTouchedListener(PropertyTouchedListener propertyTouchedListener) {
        propertyTouchedListeners.add(propertyTouchedListener);
    }

    public void actCompleted(List<VOEntity> elements) {
    }
}